﻿namespace AddressBook.Business.Person
{
	#region "Using Statement"
	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.ComponentModel.DataAnnotations;
	using System.Linq;
	using System.Text;

	using Csla;
	using Csla.Data;
	using Csla.Validation;

	using AddressBook.Business.Contact;
	using AddressBook.Business.Event;
	#endregion

	#region "IPerson interface"

	public interface IPerson
	{
		String Title { get; }
		String FirstName { get; }
		String MiddleName { get; }
		String LastName { get; }
		String Suffix { get; }
		String FormalName { get; }
		String FullName { get; }
		String Name { get; }
	}

	#endregion

	[Serializable]
	public class Contact : BusinessBase<Contact>, IPerson
	{
		#region "Business Methods"

		#region "Private Static Member Variables"

		private static PropertyInfo<Int32> IdProperty = RegisterProperty<Int32>(c => c.Id);
		private static PropertyInfo<String> TitleProperty = RegisterProperty<String>(c => c.Title);
		private static PropertyInfo<String> FirstNameProperty = RegisterProperty<String>(c => c.FirstName);
		private static PropertyInfo<String> MiddleNameProperty = RegisterProperty<String>(c => c.MiddleName);
		private static PropertyInfo<String> LastNameProperty = RegisterProperty<String>(c => c.LastName);
		private static PropertyInfo<String> SuffixProperty = RegisterProperty<String>(c => c.Suffix);
		private static PropertyInfo<ContactAddresses> AddressesProperty = RegisterProperty<ContactAddresses>(c => c.Addresses);
		private static PropertyInfo<ContactEmailAddresses> EmailAddressesProperty = RegisterProperty<ContactEmailAddresses>(c => c.EmailAddresses);
		private static PropertyInfo<ContactPhoneNumbers> PhoneNumbersProperty = RegisterProperty<ContactPhoneNumbers>(c => c.PhoneNumbers);

		#endregion

		#region "Public Properties"

		[ScaffoldColumn(false)]
		[DisplayName("Id")]
		public Int32 Id
		{
			get { return GetProperty(IdProperty); }
			internal set { LoadProperty(IdProperty, value); }
		}

		[StringLength(8, ErrorMessage = "A contacts title cannot be more than 8 characters.")]
		[DisplayName("Title")]
		public String Title
		{
			get { return GetProperty(TitleProperty); }
			set { SetProperty(TitleProperty, value); }
		}

		[Required(ErrorMessage = "A first name is required.")]
		[StringLength(50, ErrorMessage = "A contact's first name cannot be more than 50 characters.")]
		[DisplayName("First Name")]
		public String FirstName
		{
			get { return GetProperty(FirstNameProperty); }
			set { SetProperty(FirstNameProperty, value); }
		}

		[StringLength(50, ErrorMessage = "A contact's middle name cannot be more than 50 characters.")]
		[DisplayName("Middle Name")]
		public String MiddleName
		{
			get { return GetProperty(MiddleNameProperty); }
			set { SetProperty(MiddleNameProperty, value); }
		}

		[Required(ErrorMessage = "A last name is required.")]
		[StringLength(50, ErrorMessage = "A contact's last name cannot be more than 50 characters.")]
		[DisplayName("Last Name")]
		public String LastName
		{
			get { return GetProperty(LastNameProperty); }
			set { SetProperty(LastNameProperty, value); }
		}

		[StringLength(10, ErrorMessage = "A contact's suffix cannot be more than 10 characters.")]
		[DisplayName("Suffix")]
		public String Suffix
		{
			get { return GetProperty(SuffixProperty); }
			set { SetProperty(SuffixProperty, value); }
		}

		[ScaffoldColumn(false)]
		public ContactAddresses Addresses
		{
			get
			{
				if (!(FieldManager.FieldExists(AddressesProperty)))
				{
					LoadProperty(AddressesProperty, ContactAddresses.NewContactAddresses());
				}

				return GetProperty(AddressesProperty);
			}
		}

		[ScaffoldColumn(false)]
		public ContactEmailAddresses EmailAddresses
		{
			get
			{
				if (!(FieldManager.FieldExists(EmailAddressesProperty)))
				{
					LoadProperty(EmailAddressesProperty, ContactEmailAddresses.NewContactEmailAddresses());
				}

				return GetProperty(EmailAddressesProperty);
			}
		}

		[ScaffoldColumn(false)]
		public ContactPhoneNumbers PhoneNumbers
		{
			get
			{
				if (!(FieldManager.FieldExists(PhoneNumbersProperty)))
				{
					LoadProperty(PhoneNumbersProperty, ContactPhoneNumbers.NewContactPhoneNumbers());
				}

				return GetProperty(PhoneNumbersProperty);
			}
		}

		[ScaffoldColumn(false)]
		public string FormalName
		{
			get
			{
				if (String.IsNullOrEmpty(this.MiddleName))
				{
					return String.Format("{0} {1} {2} {3}", this.Title, this.FirstName, this.LastName, this.Suffix).Trim();
				}

				return String.Format("{0} {1} {2} {3} {4}", this.Title, this.FirstName, this.MiddleName, this.LastName, this.Suffix).Trim();
			}
		}

		[ScaffoldColumn(false)]
		public string FullName
		{
			get
			{
				return Contact.GetFullName(this.FirstName, this.MiddleName, this.LastName, this.Suffix);
			}
		}

		[ScaffoldColumn(false)]
		public string Name
		{
			get
			{
				return String.Format("{0} {1}", this.FirstName, this.LastName);
			}
		}

		[ScaffoldColumn(false)]
		public string HomeAddress
		{
			get
			{
				if ((Addresses.Select(a => a.AddressTypeId == 1).ToList()).Count > 0)
				{
					return (Addresses.First(a => a.AddressTypeId == 1)).FullAddress;
				}
				else
				{
					return String.Empty;
				}
			}
		}

		[ScaffoldColumn(false)]
		public string HomePhone
		{
			get
			{
				if ((PhoneNumbers.Select(p => p.PhoneNumberTypeId == 1).ToList()).Count > 0)
				{
					return PhoneNumber.FormatPhone((PhoneNumbers.First(p => p.PhoneNumberTypeId == 1)).PhoneNumber);
				}
				else
				{
					return String.Empty;
				}
			}
		}

		[ScaffoldColumn(false)]
		public string CellPhone
		{
			get
			{
				if ((PhoneNumbers.Select(p => p.PhoneNumberTypeId == 2).ToList()).Count > 0)
				{
					return PhoneNumber.FormatPhone((PhoneNumbers.First(p => p.PhoneNumberTypeId == 2)).PhoneNumber);
				}
				else
				{
					return String.Empty;
				}
			}
		}

		#endregion

		public override string ToString()
		{
			return String.Concat(FirstName, " ", LastName);
		}

		#endregion

		#region "Business Rules"

		protected override void AddBusinessRules()
		{
			ValidationRules.AddRule(CommonRules.StringMaxLength, new CommonRules.MaxLengthRuleArgs(TitleProperty, 8));

			ValidationRules.AddRule(CommonRules.StringRequired, new RuleArgs(FirstNameProperty));
			ValidationRules.AddRule(CommonRules.StringMaxLength, new CommonRules.MaxLengthRuleArgs(FirstNameProperty, 50));

			ValidationRules.AddRule(CommonRules.StringMaxLength, new CommonRules.MaxLengthRuleArgs(MiddleNameProperty, 50));

			ValidationRules.AddRule(CommonRules.StringRequired, new RuleArgs(LastNameProperty));
			ValidationRules.AddRule(CommonRules.StringMaxLength, new CommonRules.MaxLengthRuleArgs(LastNameProperty, 50));

			ValidationRules.AddRule(CommonRules.StringMaxLength, new CommonRules.MaxLengthRuleArgs(SuffixProperty, 10));
		}

		#endregion

		#region  "Factory Methods"

		public static Contact NewContact()
		{
			return DataPortal.Create<Contact>();
		}

		public static Contact GetContact(Int32 id)
		{
			return DataPortal.Fetch<Contact>(new SingleCriteria<Contact, Int32>(id));
		}

		public static void DeleteContact(Int32 id)
		{
			DataPortal.Delete(new SingleCriteria<Contact, Int32>(id));
		}

		private Contact()
		{ /* require use of factory methods */ }

		public static String GetFullName(String firstName, String middleName, String lastName, String suffix)
		{
			if (String.IsNullOrEmpty(middleName) && String.IsNullOrEmpty(suffix))
			{
				return String.Format("{0} {1}", firstName, lastName);
			}
			else if (!String.IsNullOrEmpty(middleName) && String.IsNullOrEmpty(suffix))
			{
				return String.Format("{0} {1} {2}", firstName, middleName, lastName);
			}
			else if (String.IsNullOrEmpty(middleName) && !String.IsNullOrEmpty(suffix))
			{
				return String.Format("{0} {1} {2}", firstName, lastName, suffix);
			}

			return String.Format("{0} {1} {2} {3}", firstName, middleName, lastName, suffix);
		}

		#endregion

		#region "Data Access"

		[RunLocal()]
		protected override void DataPortal_Create()
		{
			using (BypassPropertyChecks)
			{
				ValidationRules.CheckRules();
			}
		}

		private void DataPortal_Fetch(SingleCriteria<Contact, Int32> criteria)
		{
			using (var ctx = ContextManager<AddressBook.Business.Model.AddressBookDataContext>.GetManager(AddressBook.Business.Model.Database.AddressBook))
			{
				// get contact data
				var contact = ctx.DataContext.Contacts.First(c => c.Id == criteria.Value);

				using (BypassPropertyChecks)
				{
					this.Id = contact.Id;
					this.FirstName = contact.FirstName;
					this.MiddleName = contact.MiddleName;
					this.LastName = contact.LastName;
					this.Title = contact.Title;
					this.Suffix = contact.Suffix;

					// get child data
					LoadProperty(AddressesProperty,
																	 ContactAddresses.GetContactAddresses(
																			contact.ContactAddresses.ToArray())
																	 );

					LoadProperty(EmailAddressesProperty,
											 ContactEmailAddresses.GetContactEmailAddresses(
												contact.ContactEmailAddresses.ToArray())
											 );

					LoadProperty(PhoneNumbersProperty,
																	 ContactPhoneNumbers.GetContactPhoneNumbers(
																			contact.ContactPhoneNumbers.ToArray())
																	 );
				}
			}
		}

		[Transactional(TransactionalTypes.TransactionScope)]
		protected override void DataPortal_Insert()
		{
			using (var ctx = ContextManager<AddressBook.Business.Model.AddressBookDataContext>.GetManager(AddressBook.Business.Model.Database.AddressBook))
			{
				// insert contact data
				using (BypassPropertyChecks)
				{
					AddressBook.Business.Model.Contact contact = new AddressBook.Business.Model.Contact();
					contact.Title = this.Title;
					contact.FirstName = this.FirstName;
					contact.MiddleName = this.MiddleName;
					contact.LastName = this.LastName;
					contact.Suffix = this.Suffix;

					ctx.DataContext.Contacts.InsertOnSubmit(contact);
					ctx.DataContext.SubmitChanges();

					this.Id = contact.Id;
				}

				// update child objects
				FieldManager.UpdateChildren(this);
			}
		}

		[Transactional(TransactionalTypes.TransactionScope)]
		protected override void DataPortal_Update()
		{
			using (var ctx = ContextManager<AddressBook.Business.Model.AddressBookDataContext>.GetManager(AddressBook.Business.Model.Database.AddressBook))
			{
				AddressBook.Business.Model.Contact contact = ctx.DataContext.Contacts.First(c => c.Id == this.Id);

				// update contact data
				using (BypassPropertyChecks)
				{
					contact.Title = this.Title;
					contact.FirstName = this.FirstName;
					contact.MiddleName = this.MiddleName;
					contact.LastName = this.LastName;
					contact.Suffix = this.Suffix;

					ctx.DataContext.SubmitChanges();
				}

				// update child objects
				FieldManager.UpdateChildren(this);
			}
		}

		[Transactional(TransactionalTypes.TransactionScope)]
		protected override void DataPortal_DeleteSelf()
		{
			DataPortal_Delete(new SingleCriteria<Contact, Int32>(this.Id));
		}

		[Transactional(TransactionalTypes.TransactionScope)]
		private void DataPortal_Delete(SingleCriteria<Contact, Int32> criteria)
		{
			using (var ctx = ContextManager<AddressBook.Business.Model.AddressBookDataContext>.GetManager(AddressBook.Business.Model.Database.AddressBook))
			{
				// get contact data
				AddressBook.Business.Model.Contact contact = ctx.DataContext.Contacts.First(c => c.Id == criteria.Value);

				foreach (AddressBook.Business.Model.Guest guest in ctx.DataContext.Guests.Where(g => g.GuestId == contact.Id && g.GuestTypeId == (int)GuestType.Contact))
				{
					ctx.DataContext.Guests.DeleteOnSubmit(guest);
				}

				foreach (AddressBook.Business.Model.FamilyMember member in ctx.DataContext.FamilyMembers.Where(m => m.ContactId == contact.Id))
				{
					ctx.DataContext.FamilyMembers.DeleteOnSubmit(member);
				}

				ctx.DataContext.SubmitChanges();

				// delete contact data
				ctx.DataContext.Contacts.DeleteOnSubmit(contact);
				ctx.DataContext.SubmitChanges();

				// reset child list field
				LoadProperty(AddressesProperty, ContactAddresses.NewContactAddresses());
				LoadProperty(EmailAddressesProperty, ContactEmailAddresses.NewContactEmailAddresses());
				LoadProperty(PhoneNumbersProperty, ContactPhoneNumbers.NewContactPhoneNumbers());
			}
		}

		#endregion
	}
}
